﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dokan;
using Affirma.ThreeSharp.Wrapper;
using System.IO;
using System.Xml;
using log4net;
using Affirma.ThreeSharp;
using System.Collections;

namespace CloudDrive.S3
{
    public class S3DriveOperations : DokanOperations
    {
        private ILog _Log;
        private FileProxy _Proxy;
        //private RemoteFileManager _FileManager;
        //private FileCache _FileCache;

        public S3DriveOperations(FileProxy proxy)
        {
            _Proxy = proxy;
            _Log = log4net.LogManager.GetLogger(typeof(S3DriveOperations));
        }


        #region DokanOperations Members

        public int CreateFile(string filename, System.IO.FileAccess access, System.IO.FileShare share, System.IO.FileMode mode, System.IO.FileOptions options, DokanFileInfo info)
        {
            _Log.Info(string.Format("CreateFile {0} with mode {1} options {2}", filename, mode, options));

            if (filename == "\\")
                return DokanNet.DOKAN_SUCCESS;
            try
            {
                var fileExists = filename == "\\" || !(_Proxy.GetFileInfo(filename) == null);
                var file = _Proxy.Create(filename, mode);
                if (!info.IsDirectory)
                    info.IsDirectory = file.IsDirectory;
                info.Context = file;
                if (mode == FileMode.OpenOrCreate && fileExists)
                    return -183;
                return DokanNet.DOKAN_SUCCESS;
            }
            catch (FileNotFoundException ex)
            {
                _Log.Warn("File " + filename + " not found");
                return -DokanNet.ERROR_FILE_NOT_FOUND;
            }
            catch (PathTooLongException ex)
            {
                _Log.Error("Path too long " + filename, ex);
                return -DokanNet.ERROR_INVALID_NAME;
            }
            catch (IOException ex)
            {
                _Log.Error("File exists", ex);
                return -DokanNet.ERROR_ALREADY_EXISTS;
            }
            //return DokanNet.DOKAN_ERROR;
        }

        public int OpenDirectory(string filename, DokanFileInfo info)
        {
            _Log.Info("OpenDirectory '" + filename + "'");
            if (!info.IsDirectory)
                info.IsDirectory = filename.EndsWith("\\");
            return DokanNet.DOKAN_SUCCESS;
        }

        public int CreateDirectory(string filename, DokanFileInfo info)
        {
            _Log.Info(string.Format("CreateDirectory {0}", filename));
            return DokanNet.DOKAN_SUCCESS;
        }

        public int Cleanup(string filename, DokanFileInfo info)
        {
            _Log.Info(string.Format("Cleanup {0}", filename));
            if (!info.IsDirectory)
                info.IsDirectory = filename.EndsWith("\\");
            return DokanNet.DOKAN_SUCCESS;
        }

        public int CloseFile(string filename, DokanFileInfo info)
        {
            _Log.Info(string.Format("CloseFile {0}", filename));
            if (!info.IsDirectory)
                info.IsDirectory = filename.EndsWith("\\");
            return DokanNet.DOKAN_SUCCESS;
        }

        public int ReadFile(string filename, byte[] buffer, ref uint readBytes, long offset, DokanFileInfo info)
        {
            _Log.Info(string.Format("ReadFile {0}, bufferLength {1}, readBytes {2}, offset {3}", filename, buffer.Length, readBytes, offset));
            //byte[] bytes = _Proxy.ReadBytes(filename, offset, readBytes);
            //bytes.CopyTo(buffer, 0);
            return DokanNet.DOKAN_SUCCESS;
        }

        public int WriteFile(string filename, byte[] buffer, ref uint writtenBytes, long offset, DokanFileInfo info)
        {
            _Log.Info(string.Format("WriteFile {0}, bufferLength {1}, writtenBytes: {2}, offset {3}", filename, buffer.Length, writtenBytes, offset));
            return DokanNet.DOKAN_SUCCESS;
        }

        public int FlushFileBuffers(string filename, DokanFileInfo info)
        {
            _Log.Info(string.Format("FlushFileBuffers {0} w", filename));
            return DokanNet.DOKAN_ERROR;
        }

        private void PopulateFileInformation(FileInformation fileinfo, FileInfo file)
        {
            fileinfo.Attributes = file.IsDirectory ? FileAttributes.Directory : FileAttributes.Normal;
            fileinfo.CreationTime = file.LastModified.AddDays(-2);
            fileinfo.FileName = file.FileName;
            fileinfo.LastAccessTime = file.LastModified.AddDays(-1);
            fileinfo.LastWriteTime = file.LastModified.AddDays(-1);
            fileinfo.Length = file.Length;
        }
        public int GetFileInformation(string filename, FileInformation fileinfo, DokanFileInfo info)
        {
            _Log.Info(string.Format("GetFileInformation {0}", filename));
            FileInfo file;
            if (filename == "\\")
            {
                file = new FileInfo()
                {
                    FullName = "\\",
                    LastModified = DateTime.Now,
                    Length = 0
                };
            }
            else
                file = _Proxy.GetFileInfo(filename);
            if (file == null)
            {
                _Log.Error("File not found");
                return -DokanNet.ERROR_FILE_NOT_FOUND;
            }

            PopulateFileInformation(fileinfo, file);

            if (!info.IsDirectory)
                info.IsDirectory = file.IsDirectory;
            
            return DokanNet.DOKAN_SUCCESS;
        }

        public int FindFiles(string filename, System.Collections.ArrayList files, DokanFileInfo info)
        {
            _Log.Info(string.Format("FindFiles {0}", filename));
            if (!filename.EndsWith("\\"))
                filename = filename + "\\";
            foreach (var file in _Proxy.Files)
            {
                if (!file.Directory.Equals(filename, StringComparison.CurrentCultureIgnoreCase))
                    continue;
                _Log.Info("File found " + file.FullName);
                FileInformation fileInfo = new FileInformation();
                PopulateFileInformation(fileInfo, file);
                files.Add(fileInfo);
            }
            return DokanNet.DOKAN_SUCCESS;
        }

        public int SetFileAttributes(string filename, System.IO.FileAttributes attr, DokanFileInfo info)
        {
            _Log.Info(string.Format("SetFileAttributes {0}, {1}", filename, attr));
            return DokanNet.DOKAN_SUCCESS;
        }

        public int SetFileTime(string filename, DateTime ctime, DateTime atime, DateTime mtime, DokanFileInfo info)
        {
            _Log.Info(string.Format("Create File {0}, ctime {1}, atime {2}, mtime {3}", filename, ctime, atime, mtime));
            return DokanNet.DOKAN_SUCCESS;
        }

        public int DeleteFile(string filename, DokanFileInfo info)
        {
            _Log.Info(string.Format("DeleteFile {0}", filename));
            info.IsDirectory = filename.EndsWith("\\");
            try
            {
                _Proxy.Delete(filename);
            }
            catch (FileNotFoundException ex)
            {
                _Log.Info(string.Format("File not found", ex));
                return -DokanNet.ERROR_FILE_NOT_FOUND;
            }
            return DokanNet.DOKAN_SUCCESS;
        }

        public int DeleteDirectory(string filename, DokanFileInfo info)
        {
            _Log.Info(string.Format("DeleteDirectory {0}", filename));
            return DokanNet.DOKAN_SUCCESS;
        }

        public int MoveFile(string filename, string newname, bool replace, DokanFileInfo info)
        {
            _Log.Info(string.Format("Moving file from '{0}' to '{1}' (replace: {2})", filename, newname, replace));
            return DokanNet.DOKAN_SUCCESS;
        }

        public int SetEndOfFile(string filename, long length, DokanFileInfo info)
        {
            _Log.Info("Setting end of file '" + filename + "' to " + length);
            return DokanNet.DOKAN_SUCCESS;
        }

        public int LockFile(string filename, long offset, long length, DokanFileInfo info)
        {
            return DokanNet.DOKAN_SUCCESS;
        }

        public int UnlockFile(string filename, long offset, long length, DokanFileInfo info)
        {
            return DokanNet.DOKAN_SUCCESS;
        }

        public int GetDiskFreeSpace(ref ulong freeBytesAvailable, ref ulong totalBytes, ref ulong totalFreeBytes, DokanFileInfo info)
        {
            freeBytesAvailable = 512 * 1024 * 1024;
            totalFreeBytes = 1024 * 1024 * 1024;
            totalBytes = freeBytesAvailable;
            return DokanNet.DOKAN_SUCCESS;
        }

        public int Unmount(DokanFileInfo info)
        {
            return DokanNet.DOKAN_SUCCESS;
        }

        #endregion
    }
}
